﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;

namespace LongNet.Util
{
    /// <summary>
    /// Provides a set of static methods for <see cref="String"/>.
    /// </summary>
    public static class StringExtensions
    {
        #region Static Methods

        #region Char Extensions

        /// <summary>
        /// Converts the numeric Unicode character at the specified position in a specified 
        /// string to a double-precision floating point number.
        /// </summary>
        /// <param name="s">A <see cref="System.String"/>.</param>
        /// <param name="index">The character position in s.</param>
        /// <returns>The numeric value of the character at position index in s if that character 
        /// represents a number; otherwise, -1.</returns>
        /// <exception cref="System.ArgumentNullException">s is null.</exception>
        /// <exception cref="System.ArgumentOutOfRangeException">index is less than zero or greater than the last position in s.</exception>
        public static double GetNumericValue(this string s, int index)
        {
            return char.GetNumericValue(s, index);
        }

        /// <summary>
        /// Indicates whether the character at the specified position in a specified 
        /// string is categorized as a control character.
        /// </summary>
        /// <param name="s">A <see cref="System.String"/>.</param>
        /// <param name="index">The character position in s.</param>
        /// <returns>true if the character at position index in s is a control character; otherwise, false.</returns>
        /// <exception cref="System.ArgumentNullException">s is null.</exception>
        /// <exception cref="System.ArgumentOutOfRangeException">index is less than zero or greater than the last position in s.</exception>
        public static bool IsControl(this string s, int index)
        {
            return char.IsControl(s, index);
        }

        /// <summary>
        /// Indicates whether the character at the specified position in a specified 
        /// string is categorized as a decimal digit.
        /// </summary>
        /// <param name="s">A <see cref="System.String"/>.</param>
        /// <param name="index">The character position in s.</param>
        /// <returns>true if the character at position index in s is a decimal digit; otherwise, false.</returns>
        /// <exception cref="System.ArgumentNullException">s is null.</exception>
        /// <exception cref="System.ArgumentOutOfRangeException">index is less than zero or greater than the last position in s.</exception>
        public static bool IsDigit(this string s, int index)
        {
            return char.IsDigit(s, index);
        }

        /// <summary>
        /// Indicates whether the <see cref="System.Char"/> object at the specified position in a string 
        /// is a high surrogate.
        /// </summary>
        /// <param name="s">A <see cref="System.String"/>.</param>
        /// <param name="index">The character position in s.</param>
        /// <returns>true if the numeric value of the specified character in the s parameter ranges 
        /// from U+D800 through U+DBFF; otherwise, false.</returns>
        /// <exception cref="System.ArgumentNullException">s is null.</exception>
        /// <exception cref="System.ArgumentOutOfRangeException">index is less than zero or greater than the last position in s.</exception>
        public static bool IsHighSurrogate(this string s, int index)
        {
            return char.IsHighSurrogate(s, index);
        }

        /// <summary>
        /// Indicates whether the character at the specified position in a specified 
        /// string is categorized as an alphabetic character.
        /// </summary>
        /// <param name="s">A <see cref="System.String"/>.</param>
        /// <param name="index">The character position in s.</param>
        /// <returns>true if the character at position index in s is an alphabetic character; otherwise, false.</returns>
        /// <exception cref="System.ArgumentNullException">s is null.</exception>
        /// <exception cref="System.ArgumentOutOfRangeException">index is less than zero or greater than the last position in s.</exception>
        public static bool IsLetter(this string s, int index)
        {
            return char.IsLetter(s, index);
        }

        /// <summary>
        /// Indicates whether the character at the specified position in a specified 
        /// string is categorized as an alphabetic character or a decimal digit.
        /// </summary>
        /// <param name="s">A <see cref="System.String"/>.</param>
        /// <param name="index">The character position in s.</param>
        /// <returns>true if the character at position index in s is an alphabetic character or 
        /// a decimal digit; otherwise, false.</returns>
        /// <exception cref="System.ArgumentNullException">s is null.</exception>
        /// <exception cref="System.ArgumentOutOfRangeException">index is less than zero or greater than the last position in s.</exception>
        public static bool IsLetterOrDigit(this string s, int index)
        {
            return char.IsLetterOrDigit(s, index);
        }

        /// <summary>
        /// Indicates whether the character at the specified position in a specified 
        /// string is categorized as a lowercase letter.
        /// </summary>
        /// <param name="s">A <see cref="System.String"/>.</param>
        /// <param name="index">The character position in s.</param>
        /// <returns>true if the character at position index in s is a lowercase letter; otherwise, false.</returns>
        /// <exception cref="System.ArgumentNullException">s is null.</exception>
        /// <exception cref="System.ArgumentOutOfRangeException">index is less than zero or greater than the last position in s.</exception>
        public static bool IsLower(this string s, int index)
        {
            return char.IsLower(s, index);
        }

        /// <summary>
        /// Indicates whether the <see cref="System.Char"/> object at the specified position in a string is a low surrogate.
        /// </summary>
        /// <param name="s">A <see cref="System.String"/>.</param>
        /// <param name="index">The character position in s.</param>
        /// <returns>true if the numeric value of the specified character in the s parameter ranges 
        /// from U+DC00 through U+DFFF; otherwise, false.</returns>
        /// <exception cref="System.ArgumentNullException">s is null.</exception>
        /// <exception cref="System.ArgumentOutOfRangeException">index is less than zero or greater than the last position in s.</exception>
        public static bool IsLowSurrogate(this string s, int index)
        {
            return char.IsLowSurrogate(s, index);
        }

        /// <summary>
        /// Indicates whether the character at the specified position in a specified 
        /// string is categorized as a number.
        /// </summary>
        /// <param name="s">A <see cref="System.String"/>.</param>
        /// <param name="index">The character position in s.</param>
        /// <returns>true if the character at position index in s is a number; otherwise, false.</returns>
        /// <exception cref="System.ArgumentNullException">s is null.</exception>
        /// <exception cref="System.ArgumentOutOfRangeException">index is less than zero or greater than the last position in s.</exception>
        public static bool IsNumber(this string s, int index)
        {
            return char.IsNumber(s, index);
        }

        /// <summary>
        /// Indicates whether the character at the specified position in a specified 
        /// string is categorized as a punctuation mark.
        /// </summary>
        /// <param name="s">A <see cref="System.String"/>.</param>
        /// <param name="index">The character position in s.</param>
        /// <returns>true if the character at position index in s is a punctuation mark; otherwise, false.</returns>
        /// <exception cref="System.ArgumentNullException">s is null.</exception>
        /// <exception cref="System.ArgumentOutOfRangeException">index is less than zero or greater than the last position in s.</exception>
        public static bool IsPunctuation(this string s, int index)
        {
            return char.IsPunctuation(s, index);
        }

        /// <summary>
        /// Indicates whether the character at the specified position in a specified 
        /// string is categorized as a separator character.
        /// </summary>
        /// <param name="s">A <see cref="System.String"/>.</param>
        /// <param name="index">The character position in s.</param>
        /// <returns>true if the character at position index in s is a separator character; otherwise, false.</returns>
        /// <exception cref="System.ArgumentNullException">s is null.</exception>
        /// <exception cref="System.ArgumentOutOfRangeException">index is less than zero or greater than the last position in s.</exception>
        public static bool IsSeparator(this string s, int index)
        {
            return char.IsSeparator(s, index);
        }

        /// <summary>
        /// Indicates whether the character at the specified position in a specified 
        /// string has a surrogate code point.
        /// </summary>
        /// <param name="s">A <see cref="System.String"/>.</param>
        /// <param name="index">The character position in s.</param>
        /// <returns>true if the character at position index in s is a either a high surrogate 
        /// or a low surrogate; otherwise, false.</returns>
        /// <exception cref="System.ArgumentNullException">s is null.</exception>
        /// <exception cref="System.ArgumentOutOfRangeException">index is less than zero or greater than the last position in s.</exception>
        public static bool IsSurrogate(this string s, int index)
        {
            return char.IsSurrogate(s, index);
        }

        /// <summary>
        /// Indicates whether the character at the specified position in a specified 
        /// string is categorized as a symbol character.
        /// </summary>
        /// <param name="s">A <see cref="System.String"/>.</param>
        /// <param name="index">The character position in s.</param>
        /// <returns>true if the character at position index in s is a symbol character; otherwise, false.</returns>
        /// <exception cref="System.ArgumentNullException">s is null.</exception>
        /// <exception cref="System.ArgumentOutOfRangeException">index is less than zero or greater than the last position in s.</exception>
        public static bool IsSymbol(this string s, int index)
        {
            return char.IsSymbol(s, index);
        }

        /// <summary>
        /// Indicates whether the character at the specified position in a specified 
        /// string is categorized as an uppercase letter.
        /// </summary>
        /// <param name="s">A <see cref="System.String"/>.</param>
        /// <param name="index">The character position in s.</param>
        /// <returns>true if the character at position index in s is an uppercase letter; otherwise, false.</returns>
        /// <exception cref="System.ArgumentNullException">s is null.</exception>
        /// <exception cref="System.ArgumentOutOfRangeException">index is less than zero or greater than the last position in s.</exception>
        public static bool IsUpper(this string s, int index)
        {
            return char.IsUpper(s, index);
        }

        /// <summary>
        /// Indicates whether the character at the specified position in a specified 
        /// string is categorized as white space.
        /// </summary>
        /// <param name="s">A <see cref="System.String"/>.</param>
        /// <param name="index">The character position in s.</param>
        /// <returns>true if the character at position index in s is white space; otherwise, false.</returns>
        /// <exception cref="System.ArgumentNullException">s is null.</exception>
        /// <exception cref="System.ArgumentOutOfRangeException">index is less than zero or greater than the last position in s.</exception>
        public static bool IsWhiteSpace(this string s, int index)
        {
            return char.IsWhiteSpace(s, index);
        }

        #endregion

        #region String Extensions

        /// <summary>
        /// Replaces the format item in a specified <see cref="System.String"/> with the text equivalent 
        /// of the value of a specified <see cref="System.Object"/> instance.
        /// </summary>
        /// <param name="format">A composite format string.</param>
        /// <param name="arg0">An <see cref="System.Object"/> to format.</param>
        /// <returns>A copy of format in which the first format item has been replaced by the <see cref="System.String"/> equivalent of arg0.</returns>
        /// <exception cref="System.ArgumentNullException">format is null.</exception>
        /// <exception cref="System.FormatException">
        /// The format item in format is invalid.  -or- The number indicating an argument 
        /// to format is less than zero, or greater than or equal to the number of specified 
        /// objects to format.
        /// </exception>
        public static string Format(this string format, object arg0)
        {
            return string.Format(format, arg0);
        }

        /// <summary>
        /// Replaces the format item in a specified <see cref="System.String"/> with the text equivalent
        /// of the value of a corresponding <see cref="System.Object"/> instance in a specified array.
        /// </summary>
        /// <param name="format">A composite format string.</param>
        /// <param name="args">An <see cref="System.Object"/> array containing zero or more objects to format.</param>
        /// <returns>A copy of format in which the format items have been replaced by the <see cref="System.String"/> 
        /// equivalent of the corresponding instances of <see cref="System.Object"/> in args.</returns>
        /// <exception cref="System.ArgumentNullException">format is null.</exception>
        /// <exception cref="System.FormatException">
        /// format is invalid.  -or- The number indicating an argument to format is less 
        /// than zero, or greater than or equal to the length of the args array.
        /// </exception>
        public static string Format(this string format, params object[] args)
        {
            return string.Format(format, args);
        }

        /// <summary>
        /// Replaces the format item in a specified <see cref="System.String"/> with the text equivalent
        /// of the value of two specified <see cref="System.Object"/> instances.
        /// </summary>
        /// <param name="format">A composite format string.</param>
        /// <param name="arg0">The first <see cref="System.Object"/> to format.</param>
        /// <param name="arg1">The second <see cref="System.Object"/> to format.</param>
        /// <returns>A copy of format in which the first and second format items have been replaced 
        /// by the <see cref="System.String"/> equivalents of arg0 and arg1.</returns>
        /// <exception cref="System.ArgumentNullException">format is null.</exception>
        /// <exception cref="System.FormatException">
        /// format is invalid.  -or- The number indicating an argument to format is less 
        /// than zero, or greater than or equal to the number of specified objects to format.
        /// </exception>
        public static string Format(this string format, object arg0, object arg1)
        {
            return string.Format(format, arg0, arg1);
        }

        /// <summary>
        /// Replaces the format item in a specified <see cref="System.String"/> with the text equivalent 
        /// of the value of three specified <see cref="System.Object"/> instances.
        /// </summary>
        /// <param name="format">A composite format string.</param>
        /// <param name="arg0">The first <see cref="System.Object"/> to format.</param>
        /// <param name="arg1">The second <see cref="System.Object"/> to format.</param>
        /// <param name="arg2">The third <see cref="System.Object"/> to format.</param>
        /// <returns>A copy of format in which the first, second, and third format items have 
        /// been replaced by the <see cref="System.String"/> equivalents of arg0, arg1, and arg2.</returns>
        /// <exception cref="System.ArgumentNullException">format is null.</exception>
        /// <exception cref="System.FormatException">
        /// format is invalid.  -or- The number indicating an argument to format is less 
        /// than zero, or greater than or equal to the number of specified objects to format.
        /// </exception>
        public static string Format(this string format, object arg0, object arg1, object arg2)
        {
            return string.Format(format, arg0, arg1, arg2);
        }

        /// <summary>
        /// Creates a new instance of <see cref="System.String"/> with the same value as a specified <see cref="System.String"/>.
        /// </summary>
        /// <param name="str">The <see cref="System.String"/> to copy.</param>
        /// <returns>A new <see cref="System.String"/> with the same value as str.</returns>
        /// <exception cref="System.ArgumentNullException">str is null.</exception>
        public static string Copy(this string str)
        {
            return string.Copy(str);
        }

        /// <summary>
        /// Retrieves the system's reference to the specified <see cref="System.String"/>.
        /// </summary>
        /// <param name="str">A <see cref="System.String"/>.</param>
        /// <returns>If the value of str is already interned, the system's reference is returned; 
        /// otherwise, a new reference to a string with the value of str is returned.</returns>
        /// <exception cref="System.ArgumentNullException">str is null.</exception>
        public static string Intern(this string str)
        {
            return string.Intern(str);
        }

        /// <summary>
        /// Retrieves a reference to a specified <see cref="System.String"/>.
        /// </summary>
        /// <param name="str">A <see cref="System.String"/>.</param>
        /// <returns>A <see cref="System.String"/> reference to str if it is in the common language runtime 
        /// "intern pool"; otherwise null.</returns>
        /// <exception cref="System.ArgumentNullException">str is null.</exception>
        public static string IsInterned(this string str)
        {
            return string.IsInterned(str);
        }

        #endregion


        #region Other Extensions

        /// <summary>
        /// Gets a 32-bit integer that represents the bytes length of string.
        /// </summary>
        /// <param name="str">A string.</param>
        /// <returns>A 32-bit integer that represents the bytes length of string.</returns>
        public static int BytesLength(this string str)
        {
            return Encoding.Default.GetBytes(str).Length;
        }

        /// <summary>
        /// Gets a 64-bit integer that represents the bytes length of the string.
        /// </summary>
        /// <param name="str">A string.</param>
        /// <returns>A 64-bit integer that represents the bytes length of string.</returns>
        public static long BytesLongLength(this string str)
        {
            return Encoding.Default.GetBytes(str).LongLength;
        }

        /// <summary>
        /// Indicates whether the specified <see cref="System.String"/> object is null or 
        ///  an empty string ("").
        /// </summary>
        /// <param name="str">A <see cref="System.String"/> reference.</param>
        /// <returns>true if the value parameter is null or an empty string (""); otherwise, false.</returns>
        public static bool IsNullOrEmpty(this string str)
        {
            return (str == null || str.Length == 0);
        }

        /// <summary>
        /// Indicates whether the specified <see cref="System.String"/> object is null or 
        ///  an empty string ("") after trimed.
        /// </summary>
        /// <param name="str">A <see cref="System.String"/> reference.</param>
        /// <returns>true if the value parameter is null or an empty string ("") after trimed; otherwise, false.</returns>
        public static bool IsNullOrTrimedEmpty(this string str)
        {
            return (str == null || str.Trim().Length == 0);
        }


        /// <summary>
        /// Indicates whether the regular expression finds a match in the input string 
        /// using the regular expression specified in the pattern parameter.
        /// </summary>
        /// <param name="str">The string to search for a match.</param>
        /// <param name="pattern">The regular expression pattern to match.</param>
        /// <returns>true if the regular expression finds a match; otherwise, false.</returns>
        /// <exception cref="System.ArgumentNullException">str is null.  -or- pattern is null.</exception>
        public static bool IsMatch(this string str, string pattern)
        {
            return str == null ? false : Regex.IsMatch(str, pattern);
        }

        /// <summary>
        /// Indicates whether the regular expression finds a match in the input string,
        /// using the regular expression specified in the pattern parameter and the matching 
        /// options supplied in the options parameter.
        /// </summary>
        /// <param name="str">The string to search for a match.</param>
        /// <param name="pattern">The regular expression pattern to match.</param>
        /// <param name="options">A bitwise OR combination of <see cref="System.Text.RegularExpressions.RegexOptions"/> enumeration values.</param>
        /// <returns>true if the regular expression finds a match; otherwise, false.</returns>
        /// <exception cref="System.ArgumentNullException">str is null.  -or- pattern is null.</exception>
        /// <exception cref="System.ArgumentOutOfRangeException">options is not a valid <see cref="System.Text.RegularExpressions.RegexOptions"/> value.</exception>
        public static bool IsMatch(this string str, string pattern, RegexOptions options)
        {
            return str == null ? false : Regex.IsMatch(str, pattern, options);
        }

        /// <summary>
        /// Searches the specified input string for an occurrence of the regular expression 
        /// supplied in the pattern parameter.
        /// </summary>
        /// <param name="str">The string to be tested for a match.</param>
        /// <param name="pattern">The regular expression pattern to match.</param>
        /// <returns>A regular expression <see cref="System.Text.RegularExpressions.Match"/> object.</returns>
        /// <exception cref="System.ArgumentException">Regular expression parsing error.</exception>
        public static string Match(this string str, string pattern)
        {
            return str == null ? string.Empty : Regex.Match(str, pattern).Value;
        }

        /// <summary>
        /// Searches the input string for an occurrence of the regular expression supplied 
        /// in a pattern parameter with matching options supplied in an options parameter.
        /// </summary>
        /// <param name="str">The string to be tested for a match.</param>
        /// <param name="pattern">The regular expression pattern to match.</param>
        /// <param name="options">A bitwise OR combination of RegexOption enumeration values.</param>
        /// <returns>A regular expression <see cref="System.Text.RegularExpressions.Match"/> object.</returns>
        public static string Match(this string str, string pattern, RegexOptions options)
        {
            return str == null ? string.Empty : Regex.Match(str, pattern, options).Value;
        }

        /// <summary>
        /// Indicates whether the specified <see cref="System.String"/> object is byte.
        /// </summary>
        /// <param name="str">A <see cref="System.String"/> reference.</param>
        /// <returns>true if the value parameter is byte; otherwise, false.</returns>
        public static bool IsByte(this string str)
        {
            byte result;
            return byte.TryParse(str, out result);
        }

        /// <summary>
        /// Indicates whether the specified <see cref="System.String"/> object is int.
        /// </summary>
        /// <param name="str">A <see cref="System.String"/> reference.</param>
        /// <returns>true if the value parameter is int; otherwise, false.</returns>
        public static bool IsInt(this string str)
        {
            int result;
            return int.TryParse(str, out result);
        }

        /// <summary>
        /// Indicates whether the specified <see cref="System.String"/> object is long.
        /// </summary>
        /// <param name="str">A <see cref="System.String"/> reference.</param>
        /// <returns>true if the value parameter is long; otherwise, false.</returns>
        public static bool IsLong(this string str)
        {
            long result;
            return long.TryParse(str, out result);
        }

        /// <summary>
        /// Indicates whether the specified <see cref="System.String"/> object is float.
        /// </summary>
        /// <param name="str">A <see cref="System.String"/> reference.</param>
        /// <returns>true if the value parameter is float; otherwise, false.</returns>
        public static bool IsFloat(this string str)
        {
            float result;
            return float.TryParse(str, out result);
        }

        /// <summary>
        /// Indicates whether the specified <see cref="System.String"/> object is double.
        /// </summary>
        /// <param name="str">A <see cref="System.String"/> reference.</param>
        /// <returns>true if the value parameter is double; otherwise, false.</returns>
        public static bool IsDouble(this string str)
        {
            double result;
            return double.TryParse(str, out result);
        }

        /// <summary>
        /// Indicates whether the specified <see cref="System.String"/> object is decimal.
        /// </summary>
        /// <param name="str">A <see cref="System.String"/> reference.</param>
        /// <returns>true if the value parameter is decimal; otherwise, false.</returns>
        public static bool IsDecimal(this string str)
        {
            decimal result;
            return decimal.TryParse(str, out result);
        }


        /// <summary>
        /// Converts a string to Camel Casing(the first character to lower case).
        /// </summary>
        /// <remarks>Camel Casing convention capitalizes the first character of each word except the first word.</remarks>
        /// <param name="str">A <see cref="System.String"/> reference.</param>
        /// <returns>A Camel Casing string.</returns>
        public static string ToCamelCase(this string str)
        {
            return Utils.StringToCamelCase(str);
        }

        /// <summary>
        /// Converts a string to Pascal Casing(the first character to upper case).
        /// </summary>
        /// <remarks>Pascal Casing convention capitalizes the first character of each word (including acronyms over two letters in length).</remarks>
        /// <param name="str">A <see cref="System.String"/> reference.</param>
        /// <returns>A Pascal Casing string.</returns>
        public static string ToPascalCase(this string str)
        {
            return Utils.StringToPascalCase(str);
        }

        /// <summary>
        /// Converts a string of Json to the supplied object type. 
        /// You will need to cast this object back to its expected type. 
        /// </summary>
        /// <param name="json">Json representing a .NET object.</param>
        /// <param name="type">The type of object which the Json represents.</param>
        /// <returns>A instance of object or null if the value of json is null or empty.</returns>
        public static object FromJsonString(this string json, Type type)
        {
            return Serializer.ConvertFromJsonString(json, type);
        }

        /// <summary>
        /// Converts a string of Json to the supplied object type. 
        /// You will need to cast this object back to its expected type. 
        /// </summary>
        /// <param name="json">Json representing a .NET object.</param>
        /// <param name="type">The type of object which the Json represents.</param>
        /// <param name="knownTypes">
        /// An <see cref="T:IEnumerable`1"/> of <see cref="T:System.Type"/> that contains 
        /// the types that may be present in the object graph.
        /// </param>
        /// <returns>A instance of object or null if the value of json is null or empty.</returns>
        public static object FromJsonString(this string json, Type type, IEnumerable<Type> knownTypes)
        {
            return Serializer.ConvertFromJsonString(json, type, knownTypes);
        }

        /// <summary>
        /// Converts a string of Json to the supplied object type.
        /// </summary>
        /// <typeparam name="T">The type to return.</typeparam>
        /// <param name="json">Json representing a .NET object.</param>
        /// <returns>A instance of T or null if the value of json is null or empty.</returns>
        public static T FromJsonString<T>(this string json)
        {
            return (T)Serializer.ConvertFromJsonString(json, typeof(T));
        }

        /// <summary>
        /// Converts a string of Json to the supplied object type.
        /// </summary>
        /// <typeparam name="T">The type to return.</typeparam>
        /// <param name="json">Json representing a .NET object.</param>
        /// <param name="knownTypes">
        /// An <see cref="T:IEnumerable`1"/> of <see cref="T:System.Type"/> that contains 
        /// the types that may be present in the object graph.
        /// </param>
        /// <returns>A instance of T or null if the value of json is null or empty.</returns>
        public static T FromJsonString<T>(this string json, IEnumerable<Type> knownTypes)
        {
            return (T)Serializer.ConvertFromJsonString(json, typeof(T), knownTypes);
        }

        #endregion

        #endregion
    }
}
